package com.mostka.rpc.rebind;

import java.io.PrintWriter;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JRealClassType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.json.client.*;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.mostka.rpc.client.WsRpc;
import com.mostka.rpc.util.InstanceCreator;
import com.mostka.rpc.client.WsRpcObject;

public class WsRpcClientGenerator  extends Generator{

	@Override
	public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException {
		JClassType classType;
		try {
			classType = context.getTypeOracle().getType(typeName);
			SourceWriter writer = generateMySource(classType, context, logger);
			if (writer == null)return typeName + "__WsRpcGenerated";
			writer.commit(logger);			
			return typeName + "__WsRpcGenerated";
		} catch (NotFoundException e) {
			e.printStackTrace();
			throw new UnableToCompleteException();
		}
	}

	private SourceWriter getSourceClientWriter(JClassType classType, GeneratorContext context, TreeLogger logger) {
		String packageName = classType.getPackage().getName();
		String simpleName = classType.getSimpleSourceName() + "__WsRpcGenerated";
		
		ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, simpleName);
		
		composer.setSuperclass(classType.getSimpleSourceName());

		composer.addImport(JavaScriptObject.class.getCanonicalName());
		composer.addImport(JSONArray.class.getCanonicalName());
		composer.addImport(JSONObject.class.getCanonicalName());
		composer.addImport(JSONString.class.getCanonicalName());
        composer.addImport(WsRpc.class.getCanonicalName());
        composer.addImport(GWT.class.getCanonicalName());
        composer.addImport(JSONParser.class.getCanonicalName());
        composer.addImport(WsRpcObject.class.getCanonicalName());
        composer.addImport(JSONValue.class.getCanonicalName());
		
		PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName);
		if (printWriter == null) {
			return null;
		}
		return composer.createSourceWriter(context, printWriter);
	}
	
	private String getArhumentsAsString(JParameter[] parameters, boolean classesToo){
		String str="";
		for (int i = 0; i < parameters.length; i++) {
			JParameter parameter = parameters[i];
			if (classesToo){
				str+=parameter.getType().getParameterizedQualifiedSourceName()+" ";
			}
			str+=parameter.getName();	
			if (i < parameters.length-1)
				str+=", ";
		}
		return str;
	}
	
	private SourceWriter generateMySource(JClassType classType, GeneratorContext context, TreeLogger logger) throws UnableToCompleteException{
		SourceWriter writer = getSourceClientWriter(classType, context, logger);
		if (writer==null){
			return null;
		}
		JMethod[] methods = classType.getMethods();

		writer.println("private static final String separator = \"_---_\";");
		writer.println("private "+classType.getSimpleSourceName()+" handler = null;");
		writer.println();

		writer.println("public void handleClient("+classType.getSimpleSourceName()+" handler){");
		writer.indentln("this.handler = handler;");
		writer.println("}");
		
		for (int methodPos = 0; methodPos < methods.length; methodPos++) {
			JMethod method = methods[methodPos];
			if (method.getName().equals("generateSelf")){
				continue;
			}
			writer.print("@Override public void "+method.getName()+"("+getArhumentsAsString(method.getParameters(),true)+"){}");
			writer.println();
		}
		
		writer.println();
		writer.println("public void handleMessage(String message){");
		writer.indent();
		writer.println("String[] messParts = message.split(separator);");
		writer.println("int methodPos = Integer.parseInt(messParts[1]);");
        writer.println();
        writer.println("JSONValue[] jsonObjects = new JSONValue[messParts.length-2];");
        writer.println("for (int i=0;i<messParts.length-2;i++){");
        writer.indentln("jsonObjects[i] = JSONParser.parseStrict(messParts[i+2]);");
        writer.println("}");
        writer.println();
		writer.println("switch(methodPos){");
		
		int ofset=0;
		for (int methodPos = 0; methodPos < methods.length; methodPos++) {
			JMethod method = methods[methodPos];
			if (method.getName().equals("generateSelf")){
				ofset=1;
				continue;
			}
            writer.println("case "+(methodPos-ofset)+":"+method.getName()+"Handler"+(methodPos-ofset)+"(jsonObjects);break;");
        }

        writer.println("}");
        writer.outdent();
        writer.print("}");
        writer.println();
        writer.println();


        ofset=0;
        for (int methodPos = 0; methodPos < methods.length; methodPos++) {
            JMethod method = methods[methodPos];
            if (method.getName().equals("generateSelf")){
                ofset=1;
                continue;
            }
			JParameter[] parameters = method.getParameters();

            writer.println("public void "+method.getName()+"Handler"+(methodPos-ofset)+"(JSONValue[] jsonObjects){");
			writer.indent();

            boolean JSONArrayArrayInitialized = false;
			for (int i = 0; i < parameters.length; i++) {
				JParameter parameter = parameters[i];
				String param = parameter.getType().getParameterizedQualifiedSourceName()+" "+parameter.getName();
                String paramClass = parameter.getType().getParameterizedQualifiedSourceName();
                String paramName = parameter.getName();

                if (parameter.getType().getClass().getName().endsWith(".JArrayType")){
                    if (paramClass.equals("java.lang.String[]")){
                        writer.println(param+"	= WsRpc.toJSONString(WsRpc.parseJSONArrayValues(jsonObjects["+i+"]));");
                    }else if(paramClass.equals("int[]")){
                        writer.println(param+"	= WsRpc.toJSONint(WsRpc.parseJSONArrayValues(jsonObjects["+i+"]));");
                    }else if(paramClass.equals("double[]")){
                        writer.println(param+"	= WsRpc.toJSONdouble(WsRpc.parseJSONArrayValues(jsonObjects["+i+"]));");
                    }else if(paramClass.equals("char[]")){
                        writer.println(param+"	= WsRpc.toJSONchar(WsRpc.parseJSONArrayValues(jsonObjects["+i+"]));");
                    }else if(paramClass.equals("long[]")){
                        writer.println(param+"	= WsRpc.toJSONlong(WsRpc.parseJSONArrayValues(jsonObjects["+i+"]));");
                    }else if(paramClass.equals("byte[]")){
                        writer.println(param+"	= WsRpc.toJSONbyte(WsRpc.parseJSONArrayValues(jsonObjects["+i+"]));");
                    }else if(paramClass.equals("short[]")){
                        writer.println(param+"	= WsRpc.toJSONshort(WsRpc.parseJSONArrayValues(jsonObjects["+i+"]));");
                    }else if(paramClass.equals("boolean[]")){
                        writer.println(param+"	= WsRpc.toJSONboolean(WsRpc.parseJSONArrayValues(jsonObjects["+i+"]));");
                    }else{
                        String classname = paramClass.substring(0, paramClass.length()-2);
                        try {
							if (InstanceCreator.getObjectInstance(classname) instanceof WsRpcObject){

							    String objectName = paramClass;
							    objectName = objectName.substring(0, objectName.length()-2)+"WsRpcObjectGenerated";
							    writer.println((JSONArrayArrayInitialized?"":"JSONArray ")+"array = WsRpc.parseJSONArrayValues(jsonObjects["+i+"]);");
							    writer.println(param+"=null;");
							    writer.println("if (array != null){");
							    writer.indent();
							    writer.println(objectName+"[] objectArray = new "+objectName+"[array.size()];");
							    writer.println("for (int i = 0; i < array.size(); i++) {");
							    writer.indent();
							    writer.println("if (array.get(i).isObject() != null)");
							    writer.indentln("objectArray[i] = "+objectName+".parseJSONPresenter(array.get(i).isObject());");
							    writer.println("else");
							    writer.indentln("objectArray[i] = null;");
							    writer.println("}");
							    writer.println(paramName+" = objectArray;");
							    writer.outdent();
							    writer.println("}");
							    JSONArrayArrayInitialized = true;
							}else{
							    logger.log(TreeLogger.ERROR, "only object extends WsRpcObject object are accepterd. failed for object : "+classname);
							    throw new UnableToCompleteException();
							}
						} catch (Exception e) {
							throw new UnableToCompleteException();
						}
                    }
                }else
                if (paramClass.equals("boolean")){
                    writer.println(param+" = WsRpc.parseJSONBoolean(jsonObjects["+i+"]);");
                }else
                if (parameter.getType().getClass().getName().endsWith(".JPrimitiveType")){
                    writer.println(param+" = ("+paramClass+") WsRpc.parseJSONDouble(jsonObjects["+i+"]);");
                }else
                if (paramClass.equals("java.lang.String")){
                    writer.println(param+" = WsRpc.parseJSONString(jsonObjects["+i+"]);");
                }else{
                     String classname = paramClass;
                     String str1 = ((JRealClassType) parameter.getType()).getSuperclass().getParameterizedQualifiedSourceName();
                     String str2 = WsRpcObject.class.getCanonicalName();
                    if (str1.equals(str2)){
                        writer.println(param+";");
                        writer.println("if (jsonObjects["+i+"]!=null){");
                        writer.indentln("GWT.create("+paramClass+".class);");
                        writer.indentln(paramName+" = "+paramClass+"WsRpcObjectGenerated.parseJSONPresenter(jsonObjects["+i+"].isObject());");
                        writer.println("}else{");
                        writer.indentln(paramName + " = null;");
                        writer.println("}");
                    }else{
                        logger.log(TreeLogger.ERROR, "only object extends PhpRpcObject object are accepted. failed for object : "+classname);
                        throw new UnableToCompleteException();
                    }
                }
			}
			writer.println("handler."+method.getName()+"("+getArhumentsAsString(method.getParameters(),false)+");");
			writer.outdent();
            writer.print("}");
            writer.println();
            writer.println();
		}

		return writer;
	}
	
}
